#include <stdio.h>
#include "mpi.h"
#include <time.h>
#include <math.h>

const int MASTER_RANK = 0;

int main(int  argc, char **argv )
{
    int rank, size;
    MPI_Comm new_comm;

    MPI_Init( &argc, &argv );
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_split( MPI_COMM_WORLD, rank == 0, 0, &new_comm );
    if (rank == MASTER_RANK) 
        master_io( MPI_COMM_WORLD, new_comm );
    else
        slave_io( MPI_COMM_WORLD, new_comm, rank );

    MPI_Finalize( );
    return 0;
}



/* This is the master */

int master_io( master_comm, comm )
MPI_Comm comm;
{
    int l1 = 7, l2 = 7, size, i, j, messagerIndex =0;
    double matrice[l1][l2], lineToSend[6], receivedLine[3], EPSILON = -1.0, delta = 1.0, THRESOLD = 0.02;
    MPI_Status status;
    MPI_Request request;

    MPI_Comm_size( master_comm, &size );

    srand ( time(0) );

    //Initialiser matrice la matrice
    printf( "Matrice : \n" );
    for (i=0; i<l1; i++) {
        for (j=0; j<l2; j++) {
            if( i == 0 || j == 0 || i == l1-1 || j == l2-1)
            {
                matrice[i][j] = EPSILON;
            }
            else
            {
                matrice[i][j] = rand()%8;
            }
            printf("%f ",  matrice[i][j] );
        }
        printf( "\n" );
    }
    while(fabs(delta) > THRESOLD){
        delta = -1.0;

        messagerIndex = 1;
        for (i=1; i<l1 -1; i++) {
            for (j=1; j<l2 -1; j++) {
                lineToSend[0] = i;
                lineToSend[1] = j;
                lineToSend[2] = matrice[i][j-1];
                lineToSend[3] = matrice[i][j+1];
                lineToSend[4] = matrice[i-1][j];
                lineToSend[5] = matrice[i+1][j];

                MPI_Isend(lineToSend, 6, MPI_DOUBLE, messagerIndex, 0, master_comm, &request);
                
                messagerIndex++;
                if(messagerIndex >= size){
                    messagerIndex = 1;
                }
            }
        }
        
        messagerIndex = 1;
        for (i=1; i<l1-1; i++) {
            for (j=1; j<l2-1; j++) {
                MPI_Recv( receivedLine, 3, MPI_DOUBLE, messagerIndex, 0, master_comm, &status ); 
                int x = receivedLine[1];
                int y = receivedLine[2];
        
                if( fabs(matrice[x][y] - receivedLine[0]) > delta){
                    delta = fabs(matrice[x][y] - receivedLine[0]);
                } 
                matrice[x][y] = receivedLine[0];

                messagerIndex++;
                if(messagerIndex >= size){
                    messagerIndex = 1;
                }
            }
        }

        printf("Delta is now : %f \n", delta);
    }

    printf( "Matrice finale : \n" );
    for (i=0; i<l1; i++) {
        for (j=0; j<l2; j++) {
            printf("%f ", matrice[i][j]);
        }
        printf( "\n");
    }

    lineToSend[0] = -1;
    for (i=1; i< size; i++) {
        MPI_Isend(lineToSend, 6, MPI_DOUBLE, i, 0, master_comm, &request);
    }

    return 0;
}




/* This is the slave */

int slave_io( master_comm, comm , number)
MPI_Comm comm;
{
    int needToStop = 0;
    MPI_Status status;
    double bufferReceived[6];
    double response[3];
    int i = 0;
    MPI_Request request;

    while(!needToStop){
        MPI_Recv( bufferReceived, 6, MPI_DOUBLE, MASTER_RANK, 0, master_comm, &status );    
        if(bufferReceived[0] >= 0 ){

            response[0] = 0;
            response[1] = bufferReceived[0];
            response[2] = bufferReceived[1];
            for(i = 2; i < 6; i++){
                response[0] += bufferReceived[i];
            }

            response[0] = response[0] / 4.0;
            MPI_Isend(response, 3, MPI_DOUBLE, MASTER_RANK, 0, master_comm, &request);
        }
        else
        {
            needToStop++;
        }
    }
    printf("Slave #%i terminated\n", number);
    return 0;
}
